#include "AG.h"
#include <iostream>
using namespace std;

AG::AG(){
	string aux = "config.txt";
	loadConfig(aux);
}

AG::AG(string file){
	loadConfig(file);
}

/**
	Inicializacion random de la población
*/
void AG::initRandomPopulation(){
	for(int i=0;i<this->nChrom;i++){
		for(int j=0;j<this->nGenes;j++){
			this->vChrom[i][j] = rand() % 2;
		}
	}
}

/**
	calculo de la funcion de fitness para cada individuo, ademas calcula el máximo de ellos
*/
void AG::getFitness(){
	this->maxFitness = -1111;	
	for(int i=0;i<this->nChrom;i++){
		this->fitness[i] = functionFitness(this->vChrom[i]);
		if(this->fitness[i] > this->maxFitness){
			this->maxFitness = this->fitness[i];
			this->indMaxFitness = i;
		}
	}
}

/**
	Cruza de la población
*/
void AG::cross(){
	for(int i=0;i<this->nChrom;i+=2){
		double prob = rand()/(double)RAND_MAX ;
		//cout<<"prob: "<<prob<<" this->tCross: "<<this->tCross<<endl;
		if(prob < this->tCross){
			int crossPoint = rand() % this->nGenes; //[0:nGenes]
			//cout<<"crossPoint: "<<crossPoint<<endl;
			for(int j=crossPoint;j<this->nGenes;j++){
				bool aux = this->vChrom[i][j];
				this->vChrom[i][j] = this->vChrom[i+1][j];
				this->vChrom[i+1][j] = aux;
			}		
		}
	}
}

/**
	Mutacion de la población
*/
void AG::mutation(){
	for(int i=0;i<this->nChrom;i++){
		if(rand()/(double)RAND_MAX < this->tMut){
			int mutPoint = rand()%this->nGenes;
			this->vChrom[i][mutPoint] = !this->vChrom[i][mutPoint]; //solo un punto de mutacion por cromosoma.
		}
	}
}

/**
	Seleccion de la nueva población
*/
void AG::selectNextPopulation(){
	switch(sType){
		case 0 :	{
			roulette();
			break;
		}
		case 1 : {
			window();
			break;
		}
		case 2 : {
			competency();
			break;
		}
	}
}

/**
	Ciclo de evolución
*/
vector<bool> AG::evolution(){
	//cout<<"entra a evolution"<<endl;
	initRandomPopulation();
	//printChromosome(); 
	//cout<<"inicializa random"<<endl;
	getFitness();
	//printFitness(); 
	//cout<<"pasa get fitness"<<endl;
	//cout<<"Fiteness: "<<this->maxFitness<<endl;
	int generation=0;	
	while((this->maxFitness < this->desFitness) && (generation < this->maxGenerations)){
		cout<<"Generacion: "<<generation<<endl;
		//cout<<"entra a while"<<endl;
		//printChromosome(); 
		//cout<<"before nxt"<<endl;
		selectNextPopulation();
		//printChromosome(); 
		//cout<<"sale nxt"<<endl;
		cross();
		//printChromosome(); 
		//cin.get();
		//cout<<"sale cruza"<<endl;
		mutation();
		//cout<<"sale mutation"<<endl;
		getFitness();
		//cout<<"Fiteness: "<<this->maxFitness<<endl;
		generation++;
		/*if(generation % 20 == 0) {
			printChromosome(); 
			//cin.get();
			printFitness(); 
			//cin.get();
		}*/
	}
	return this->vChrom[this->indMaxFitness];
}

/**
	Metodo de la ruleta
*/
void AG::roulette(){
	vector<vector<bool> > parents;
	parents.resize(this->nChrom);
	double sumF = 0;			
	for(int k=0;k<this->nChrom;k++) sumF += this->fitness[k];
	for(int i=0;i<this->nChrom;i++){
		double sumR = (rand()/(double)RAND_MAX) * sumF;
		double sumP = 0;
		int j = 0;
		while(j < this->nChrom){
			sumP = sumP + this->fitness[j];
			//cout<<"sumP: "<<sumP<<"  sumR: "<<sumR<<"   Fit: "<<this->fitness[j]<<endl; cin.get();
			if(sumP < sumR)
				j = j + 1;
			else  
				break;
		}
		//cout<<"j: "<<j<<endl;
		//cout<<"SumR: "<<sumR<<endl;
		//cout<<"SumF: "<<sumF<<endl; cin.get();
		parents[i] = this->vChrom[j];
	}
	copy(parents.begin(),parents.end(),this->vChrom.begin());
}

/**
	metodo de ventanas
*/
void AG::window(){
	vector<vector<bool> > parents;
	parents.resize(this->nChrom);
	sortFitness();
	for(int i=0;i<this->nChrom;i++){
		int j = rand() % i;
		parents[i] = this->vChrom[this->sortIndx[j]];				
	}
	copy(parents.begin(),parents.end(),this->vChrom.begin());
}

/**
	Ordenamiento por fitness
*/
void AG::sortFitness(){
	vector<double>auxFitness = this->fitness;
	for(int i=0;i<this->nChrom-1;i++){	
		double max = auxFitness[i];
		int indMax = i;	
		for(int j=i+1;j<this->nChrom;j++){
			if(auxFitness[j]>max){
				max = auxFitness[j];
				indMax = j;			
			}
		}
		auxFitness[indMax] = auxFitness[i];
		auxFitness[i] = max;
		this->sortIndx[i] = indMax;
	}
}

/**
	Metodo de competencias
*/
void AG::competency(){
	vector<vector<bool> > parents;
	parents.resize(this->nChrom);
	for(int i=0;i<this->nChrom;i++){	
		double max = -1111;
		int indSel;	
		for(int j=0;j<this->nComp;j++){
			int k = rand() % this->nChrom;
			if(this->fitness[k]>max){
				indSel = k;
				max = this->fitness[k];	//Se puede ver que pueden existir muchos padres iguales en la generacion.
			}
		}
		parents[i] = this->vChrom[indSel];
	}
	copy(parents.begin(),parents.end(),this->vChrom.begin());
}

/**
	Carga los datos del txt
*/
void AG::loadConfig(string file){
	ifstream archi;
	archi.open(file.c_str());
	if(archi.is_open()){
		string str_line;
		char* line;
		//Cantidad de cromosoma
		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		this->nChrom = atoi(line);
		cout<<"nchrom: "<<nChrom<<endl;
		//Genes en el cromosoma
		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		this->nGenes = atoi(line);
		cout<<"nGenes: "<<nGenes<<endl;
		//tipo de seleccion
		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		this->sType = atoi(line);
		//tasa de cruza
		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		this->tCross = atof(line);
		//tasa de mutacion
		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		this->tMut = atof(line);
		//fitness deseado
		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		this->desFitness = atof(line);
		//numero de individuos en la competencia
		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		this->nComp = atoi(line);
		//numero de individuos en brecha Generacional
		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		this->G = atoi(line);
		//Cantidad maxima de generaciones
		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		this->maxGenerations = atoi(line);
		}		
	archi.close();
	this->vChrom.resize(this->nChrom);
	for(int i=0;i<this->nChrom;i++) this->vChrom[i].resize(this->nGenes);
	this->sortIndx.resize(this->nChrom);
	this->fitness.resize(this->nChrom);
	srand(time(NULL));
}

void AG::printChromosome (){
	for(int i=0;i<this->nChrom;i++){
		for(int j=0;j<this->nGenes;j++){
			if(!vChrom[i][j]) cout<<"1";
			else cout<<"0";
		}
		cout<<endl;
	}
}

void AG::printFitness (){
	cout<<"Fitness: "<<endl;
	for(int i=0;i<this->nChrom;i++){
			cout<<fitness[i]<<endl;
			
	}
}

